{********************************************************}
{                                                        }
{                 Zeos Database Objects                  }
{            PostgreSql Transaction component            }
{                                                        }
{       Copyright (c) 1999-2001 Sergey Seroukhov         }
{    Copyright (c) 1999-2002 Zeos Development Group      }
{                                                        }
{********************************************************}

unit ZPgSqlTr;

interface

{$R *.dcr}

uses
  Classes, ZDirPgSql, ZPgSqlCon, ZTransact, ZLibPgSql, ZSqlTypes, DB, ZDBaseConst;

{$IFNDEF LINUX}
{$INCLUDE ..\Zeos.inc}
{$ELSE}
{$INCLUDE ../Zeos.inc}
{$ENDIF}

type
  { PostgreSql transaction }
  TZZPgSqlTransact = class(TZZTransact)
  private
    FNotice: AnsiString;
    function  GetDatabase: TZZPgSqlDatabase;
    function GetPID: Integer;
    procedure SetDatabase(Value: TZZPgSqlDatabase);
    function GetLastInsertOid: Oid;
    function  GetTransIsolation: TZZPgSqlTransIsolation;
    procedure SetTransIsolation(const Value: TZZPgSqlTransIsolation);
    function GetNewStyleTransactions: Boolean;
    procedure SetNewStyleTransactions(const Value: Boolean);
  public
    constructor Create(AOwner: TComponent); override;

    procedure Connect; override;
    procedure Recovery(Force: Boolean); override;
    procedure Reset;
    function ExecSql(Sql: WideString; comp: TComponent): LongInt; override;

    procedure AddMonitor(Monitor: TZZMonitor); override;
    procedure DeleteMonitor(Monitor: TZZMonitor); override;

    property PID: Integer read GetPID;
    property LastInsertOid: Oid read GetLastInsertOid;
    property Notice: AnsiString read FNotice;
  published
    property Database: TZZPgSqlDatabase read GetDatabase write SetDatabase;
    property AutoRecovery;
    property TransactSafe;
    property TransIsolation: TZZPgSqlTransIsolation read GetTransIsolation
      write SetTransIsolation;
    property NewStyleTransactions: Boolean read GetNewStyleTransactions
      write SetNewStyleTransactions default False;      
  end;

  { PostgreSQL class for asynchronous notifying}
  TZZPgSqlNotify = class(TZZNotify)
  private
    FDatabase: TZZPgSqlDatabase;
  protected
    procedure Disconnect(Sender: TObject); virtual;
    procedure SetDatabase(Value: TZZPgSqlDatabase); virtual;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    procedure Loaded; override;
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  published
    property Database: TZZPgSqlDatabase read FDatabase write SetDatabase;
  end;

implementation

{***************** TZZPgSqlTransact implementation *****************}

{ Class constructor }
constructor TZZPgSqlTransact.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FHandle := TDirPgSqlTransact.Create(nil);
  FQuery  := TDirPgSqlQuery.Create(nil, TDirPgSqlTransact(FHandle));
  AutoRecovery := True;
  FDatabaseType := dtPostgreSql;
end;

{ Get database component }
function TZZPgSqlTransact.GetDatabase: TZZPgSqlDatabase;
begin
  Result := TZZPgSqlDatabase(FDatabase);
end;

{ Set database component }
procedure TZZPgSqlTransact.SetDatabase(Value: TZZPgSqlDatabase);
begin
  inherited SetDatabase(Value);
end;

{ Retrieve backend server's process id (PID) }
function TZZPgSqlTransact.GetPID: Integer;
begin
  Result := TDirPgSqlTransact(Handle).PID;
end;

{ Get last inserted oid value }
function TZZPgSqlTransact.GetLastInsertOid: Oid;
begin
  Result := TDirPgSqlQuery(FQuery).LastInsertOid;
end;

{ Get transaction type }
function TZZPgSqlTransact.GetTransIsolation: TZZPgSqlTransIsolation;
begin
  Result := TDirPgSqlTransact(FHandle).TransIsolation;
end;

{ Set transaction type }
procedure TZZPgSqlTransact.SetTransIsolation(const Value: TZZPgSqlTransIsolation);
begin
  if Value <> TDirPgSqlTransact(FHandle).TransIsolation then
  begin
    Disconnect;
    TDirPgSqlTransact(FHandle).TransIsolation := Value;
  end;
end;

{ Execute Sql. Overrided to provide NoticeProcessor support}
function TZZPgSqlTransact.ExecSql(Sql: WideString; comp : TComponent): LongInt;
begin
  TDirPgSqlTransact(Handle).Notice := '';
  Result := inherited ExecSql(Sql, comp);
  { Setting possible notices from the database backend }
  fNotice := TDirPgSqlTransact(Handle).Notice;
end;

{ Recovery after errors }
procedure TZZPgSqlTransact.Recovery(Force: Boolean);
begin
  if AutoRecovery or Force then
    FHandle.Rollback;
end;

{ Connect to Sql-database }
procedure TZZPgSqlTransact.Connect;
begin
  inherited Connect;
{$IFNDEF PGSQL73} 
  if (Database.DefaultSchema<>'') then
    ExecSql('SET search_path='''+DataBase.DefaultSchema+''',''public''; SET DateStyle TO ''ISO''', self)
  else 
{$ENDIF}
    ExecSql('SET DateStyle TO ''ISO''', self);
end;

{ Add monitor into monitor list }
procedure TZZPgSqlTransact.AddMonitor(Monitor: TZZMonitor);
begin
  ZDirPgSql.MonitorList.AddMonitor(Monitor);
end;

{ Delete monitor from monitor list }
procedure TZZPgSqlTransact.DeleteMonitor(Monitor: TZZMonitor);
begin
  ZDirPgSql.MonitorList.DeleteMonitor(Monitor);
end;

{ Reconnect to the database server}
procedure TZZPgSqlTransact.Reset;
begin
  TDirPgSqlTransact(Handle).Reset;
end;

function TZZPgSqlTransact.GetNewStyleTransactions: Boolean;
begin
 Result := TDirPgSqlTransact(FHandle).NewStyleTransactions;
end;

procedure TZZPgSqlTransact.SetNewStyleTransactions(const Value: Boolean);
begin
 TDirPgSqlTransact(FHandle).NewStyleTransactions := Value;
end;

{***************** TZZPgSqlNotify implementation *****************}

{ Class constructor }
constructor TZZPgSqlNotify.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FHandle := TDirPgSqlNotify.Create(nil, nil);
  SetTransact(TZZPgSqlTransact.Create(Self));
  TZZPgSqlTransact(FTransact).TransactSafe:=False;
  TZZPgSqlTransact(FTransact).OnBeforeDisconnect:=Disconnect;
end;

{ Handles external Transaction's disable }
procedure TZZPgSqlNotify.Disconnect(Sender: TObject);
begin
  TZZPgSqlTransact(FTransact).OnBeforeDisconnect:=nil;
  Active:=False;
  TZZPgSqlTransact(FTransact).OnBeforeDisconnect:=Disconnect;
end;

destructor TZZPgSqlNotify.Destroy;
begin
  FTransact.Free;
  inherited Destroy;
end;

procedure TZZPgSqlNotify.SetDatabase(Value: TZZPgSqlDatabase);
begin
  if FDatabase<>Value then
  begin
    if Active then Close;
    FDatabase:=Value;
    FTransact.Database:=FDatabase;
  end;
end;

procedure TZZPgSqlNotify.Notification(AComponent: TComponent; Operation: TOperation);
begin
  inherited Notification(AComponent, Operation);
  if (AComponent = FDatabase) and (Operation = opRemove) then
    Database:=nil;
end;

{ Open autoactivated datasets }
procedure TZZPgSqlNotify.Loaded;
begin
  inherited Loaded;
  if Active and Assigned(Database) then
    Open;
end;

end.
